package personal.parser;

import personal.util.Pair;

import java.util.LinkedHashMap;

/**
 * 运算符表
 */
public class OperatorTable extends LinkedHashMap<Grammar.Terminal, Pair<Integer, OperatorTable.Associative>> {
    /**
     * 结合性
     */
    public enum Associative {
        LEFT, RIGHT
    }

    /**
     * 构造函数，由指定形式的字符串表意运算符内容、优先级、结合性。
     *
     * @param grammar     文法，注册的运算符来自这个文法
     * @param delimiter   分割符，用于划分字符串
     * @param expressions 表达式列表，每个元素形如：op  int  LEFT
     */
    public OperatorTable(Grammar grammar, String delimiter, String... expressions) {
        for (String expression : expressions) {
            String[] tokens = expression.split(delimiter);
            if (tokens.length != 3) throw new IllegalArgumentException("形式化字符串格式不对");
            try {
                Grammar.Terminal terminal = (Grammar.Terminal) grammar.getSymbol(tokens[0]);
                int level = Integer.parseInt(tokens[1]);
                Associative associative;
                if (tokens[2].equals("LEFT")) associative = Associative.LEFT;
                else if (tokens[2].equals("RIGHT")) associative = Associative.RIGHT;
                else throw new IllegalArgumentException("结合性书写错误");
                this.register(terminal, level, associative);
            } catch (NumberFormatException ex) {
                throw new IllegalArgumentException("优先级书写异常");
            } catch (ClassCastException ex) {
                throw new IllegalArgumentException("运算符书写异常");
            }

        }
    }


    /**
     * 注册运算符
     *
     * @param terminal    运算符
     * @param level       优先级
     * @param associative 结合性
     * @return 是否成功注册
     */
    public boolean register(Grammar.Terminal terminal, int level, Associative associative) {
        return this.putIfAbsent(terminal, new Pair<>(level, associative)) == null;
    }

    /**
     * @param terminal 指定运算符
     * @return 指定运算符的优先级
     */
    public int levelOf(Grammar.Terminal terminal) {
        return this.get(terminal).getFirst();
    }


    /**
     * @param terminal 指定运算符
     * @return 指定运算符的结合性
     */
    public Associative associativeOf(Grammar.Terminal terminal) {
        return this.get(terminal).getSecond();
    }

}
